package com.powernode.ssm.utils;

import com.powernode.ssm.service.DatabaseMetaDataService;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.util.validation.feature.DatabaseType;

import java.io.*;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.Date;
import java.util.stream.Collectors;

/**
 * 纯JDK + JDBC 实现的智能测试数据生成器 ✅（无第三方依赖）
 * <p>
 * 核心特性：
 * - 自动识别表主键/唯一键，作为冲突字段自动修复
 * - 支持多数据库（MySQL/Oracle/PostgreSQL）
 * - 批量插入优化（每1000条提交）
 * - 智能数据生成（名字/邮箱/手机等10+类型）
 * - 冲突字段自动修复（数字自增/字符串拼接）
 * - 详细日志记录，可追溯数据生成过程
 * <p>
 * ✅ 本次重构新增/优化：
 * - 彻底修复字符串冲突后缀错误（原：原值_1_2，现：原值_1，原值_2，原值_3...）
 * - 冲突修复时基于原始值（未冲突的值）来拼接后缀，而不是当前已修复的值
 * - 新增「先处理冲突数据→再批量插入」流程，性能更优
 * - 所有关键逻辑均有详细中文注释，便于理解与维护
 */
@Slf4j
public class PureJdbcTestDataGenerator {

    // ================================
    // 1. 数据库连接配置（支持多数据库）
    // ================================
    private static final DatabaseType DATABASE_TYPE = DatabaseType.MYSQL; // 可切换：MYSQL, ORACLE, POSTGRESQL

    private static final String JDBC_URL;
    private static final String USERNAME;
    private static final String PASSWORD;
    private static final String DRIVER_CLASS;

    static {
        switch (DATABASE_TYPE) {
            case MYSQL:
                JDBC_URL = "jdbc:mysql://localhost:3306/jdbc?useSSL=false&serverTimezone=UTC&characterEncoding=utf8";
                USERNAME = "root";
                PASSWORD = "123";
                DRIVER_CLASS = "com.mysql.cj.jdbc.Driver";
                break;
            case ORACLE:
                JDBC_URL = "jdbc:oracle:thin:@localhost:1521:ORCL";
                USERNAME = "system";
                PASSWORD = "123456";
                DRIVER_CLASS = "oracle.jdbc.driver.OracleDriver";
                break;
            case POSTGRESQL:
                JDBC_URL = "jdbc:postgresql://localhost:5432/test_db";
                USERNAME = "postgres";
                PASSWORD = "123456";
                DRIVER_CLASS = "org.postgresql.Driver";
                break;
            default:
                throw new IllegalArgumentException("不支持的数据库类型: " + DATABASE_TYPE);
        }
    }

    // 配置文件目录（如：./t_user_config.txt）
    private static final String CONFIG_DIR = "./";
    private static final String CONFIG_FILE_SUFFIX = "_config.txt";
    // ================================
    // 【修改后】使用 ClassLoader 从 classpath 加载 lexicon_config.properties
    // ================================
    private static final String LEXICON_CONFIG_FILE = "lexicon_config.properties";

    /** JDBC PreparedStatement 最大参数数量限制（如 Oracle 等驱动通常为 32767） */
    private static final int MAX_JDBC_BATCH_PARAMS = 32767;

    private static void loadLexiconConfig() {
        Properties props = new Properties();

        try (InputStream input = PureJdbcTestDataGenerator.class.getClassLoader().getResourceAsStream(LEXICON_CONFIG_FILE);
             InputStreamReader reader = new InputStreamReader(input, StandardCharsets.UTF_8)){
            props.load(reader);
            Enumeration<?> keys = props.propertyNames();
            while (keys.hasMoreElements()) {
                String key = (String) keys.nextElement();
                String value = props.getProperty(key);
                LEXICON_MAP.put(key, Arrays.asList(value.split(",")));
            }
            System.out.println("[INFO] 词库配置加载成功（来自 classpath），共加载 " + LEXICON_MAP.size() + " 个词库组");
        } catch (IOException e) {
            System.out.println("[ERROR] 加载词库配置失败: " + e.getMessage());
            initDefaultLexicon();
        }
    }

    // 动态词库（从外置配置文件加载）
    private static Map<String, List<String>> LEXICON_MAP = new HashMap<>();

    // ================================
    // 3. JDBC 类型映射（JDBC类型 -> 中文类型描述）
    // ================================
    private static final Map<Integer, String> SQL_TYPE_TO_DESC = new HashMap<>();
    static {
        SQL_TYPE_TO_DESC.put(Types.INTEGER, "整数");
        SQL_TYPE_TO_DESC.put(Types.BIGINT, "整数");
        SQL_TYPE_TO_DESC.put(Types.SMALLINT, "整数");
        SQL_TYPE_TO_DESC.put(Types.TINYINT, "整数");
        SQL_TYPE_TO_DESC.put(Types.DECIMAL, "小数");
        SQL_TYPE_TO_DESC.put(Types.NUMERIC, "小数");
        SQL_TYPE_TO_DESC.put(Types.VARCHAR, "字符串");
        SQL_TYPE_TO_DESC.put(Types.CHAR, "字符串");
        SQL_TYPE_TO_DESC.put(Types.LONGVARCHAR, "字符串");
        SQL_TYPE_TO_DESC.put(Types.NVARCHAR, "字符串");
        SQL_TYPE_TO_DESC.put(Types.DATE, "日期");
        SQL_TYPE_TO_DESC.put(Types.TIMESTAMP, "时间");
        SQL_TYPE_TO_DESC.put(Types.TIME, "时间");
    }

    // ================================
    // 4. 字段与表配置类定义
    // ================================
    static class FieldConfig {
        String fieldName;       // 字段名（数据库列名）
        String typeDesc;        // 类型描述（如：整数、字符串、邮箱）
        Integer length;         // 字段长度（如：VARCHAR(255) 的 255）
        Integer scale;          // 小数位（如：DECIMAL(10,2) 的 2）
        String randomValues;    // 随机值配置（如：男,女 或 默认 001）
        boolean isLexiconField; // 是否为词库类型字段

        FieldConfig(String fieldName, String typeDesc, Integer length, Integer scale,
                    String randomValues, boolean isLexiconField) {
            this.fieldName = fieldName;
            this.typeDesc = typeDesc;
            this.length = length;
            this.scale = scale;
            this.randomValues = randomValues;
            this.isLexiconField = isLexiconField;
        }
    }

    static class TableConfig {
        String tableName;               // 表名
        List<FieldConfig> fields = new ArrayList<>();  // 字段配置列表
        List<String> conflictFields = new ArrayList<>(); // 冲突字段列表（主键/唯一键）
        int maxRetryTimes = 3; // 新增字段，默认 3

        void addField(FieldConfig field) {
            fields.add(field);
        }

    }

    /**
     * 用于记录某个冲突字段当前已知的最大值，以及字段类型，以便于生成新的唯一值
     */
    private static class ConflictFieldMax {
        String fieldName;           // 冲突字段名，如 "user_name"
        String baseValue;           // 基础值，仅当类型为字符串拼接时有效，如 "张三"
        Integer numericSuffix;      // 数字后缀，仅当类型为字符串拼接数字后缀时有效，如 100
        Integer intValue;           // 整型最大值，如 200
        LocalDateTime dateTimeValue; // 日期时间类型最大值，如 2025-08-15T00:00
        String stringValue;         // 普通字符串最大值（无法拆解/递增时使用）
        String typeDesc;            // 字段类型描述，如 "字符串", "整数", "时间" ...
        Double doubleValue;         // ✅ 新增：小数/浮点类型最大值，如 3.14

        ConflictFieldMax(String fieldName, String typeDesc) {
            this.fieldName = fieldName;
            this.typeDesc = typeDesc;
        }
    }

    // ================================
    // 6. MAIN 入口方法
    // ================================
    public static void main(String[] args) {
        // 示例：为表生成测试数据（表名 -> 生成条数）
        Map<String, Integer> tableRowCounts = new HashMap<>();
        tableRowCounts.put("test1", 1000);   // 可添加更多表
//        tableRowCounts.put("test3", 10000);   // 可添加更多表
//        tableRowCounts.put("test4", 10000);   // 可添加更多表
        //tableRowCounts.put("user_login_attempt", 10000);

        generateAndInsertTestData(tableRowCounts);
    }

    // ================================
    // 7. 核心方法：生成并插入测试数据（主流程）
    // ================================
    public static void generateAndInsertTestData(Map<String, Integer> tableRowCounts) {
        if (tableRowCounts == null || tableRowCounts.isEmpty()) {
            System.out.println("[错误] 未指定表名或生成条数。");
            return;
        }

        Connection conn = null;
        try {
            conn = getConnection();
            conn.setAutoCommit(false); // 关闭自动提交，手动控制事务
            System.out.println("[INFO] 数据库连接成功！");
            loadLexiconConfig();

            for (Map.Entry<String, Integer> entry : tableRowCounts.entrySet()) {
                String tableName = entry.getKey();
                int rowCount = entry.getValue();

                System.out.println("\n========================================");
                System.out.println("[STEP 1] 处理表: " + tableName + "，计划生成条数: " + rowCount);

                // --- 1.1 获取表字段信息（列名、类型、精度、是否自增等）---
                List<String> dbFieldNames = new ArrayList<>();
                List<Integer> dbFieldTypes = new ArrayList<>();
                List<Integer> dbFieldPrecisions = new ArrayList<>();
                List<Integer> dbFieldScales = new ArrayList<>();
                List<Boolean> dbFieldIsAutoIncrement = new ArrayList<>();

                if (!getFieldNamesAndTypes(conn, tableName, dbFieldNames, dbFieldTypes, dbFieldPrecisions, dbFieldScales, dbFieldIsAutoIncrement)) {
                    System.out.println("[错误] 表 " + tableName + " 不存在或无字段，跳过。");
                    continue;
                }

                // --- 1.2 自动识别冲突字段（主键 + 唯一键）---
                List<String> autoConflictFields = getTablePrimaryKeyAndUniqueFields(conn, tableName);
                System.out.println("[INFO] 表 " + tableName + " 自动识别的冲突字段（主键/唯一键）: " + autoConflictFields);

                // --- 1.3 过滤掉自增字段 ---
                List<String> filteredFieldNames = new ArrayList<>();
                List<Integer> filteredFieldTypes = new ArrayList<>();
                List<Integer> filteredFieldPrecisions = new ArrayList<>();
                List<Integer> filteredFieldScales = new ArrayList<>();
                List<String> filteredConflictFields = new ArrayList<>();

                for (int i = 0; i < dbFieldNames.size(); i++) {
                    String fieldName = dbFieldNames.get(i);
                    boolean isAutoIncrement = dbFieldIsAutoIncrement.get(i);

                    if (isAutoIncrement) {
                        System.out.println("[INFO] 字段 [" + fieldName + "] 是自增字段，已自动忽略，由数据库生成。");
                        continue;
                    }

                    filteredFieldNames.add(fieldName);
                    filteredFieldTypes.add(dbFieldTypes.get(i));
                    filteredFieldPrecisions.add(dbFieldPrecisions.get(i));
                    filteredFieldScales.add(dbFieldScales.get(i));

                    if (autoConflictFields.contains(fieldName)) {
                        filteredConflictFields.add(fieldName);
                    }
                }

                if (filteredFieldNames.isEmpty()) {
                    System.out.println("[SKIP] 表 " + tableName + " 所有字段都是自增字段，无有效字段需要生成数据。");
                    continue;
                }

                // --- 1.4 加载或生成字段配置 ---
                String configFile = getTableConfigFilePath(tableName);
                TableConfig tableConfig = loadOrCreateTableConfig(conn, tableName, configFile,
                        filteredFieldNames, filteredFieldTypes, filteredFieldPrecisions, filteredFieldScales, filteredConflictFields);

                if (tableConfig == null) {
                    System.out.println("[SKIP] 表 " + tableName + " 无有效配置（已生成默认配置），跳过数据生成。请按需修改配置后重新运行。");
                    continue;
                }

                // --- 1.5 校验字段配置，提示未配置的字段 ---
                validateFieldConfig(tableConfig, filteredFieldNames, tableName);

                // --- 1.6 生成测试数据（每行为 Object[] 数组，按字段顺序）---
                List<Object[]> testDataList = generateTestDataInDbColumnOrder(tableConfig, filteredFieldNames, rowCount);

                // ✅ 保存原始数据（未冲突的原始行，用于后续冲突修复时拼接正确的后缀）
                List<Object[]> originalDataList = new ArrayList<>(testDataList); // 直接引用，因为 Object[] 是独立的

                // ✅ 处理冲突数据（如果有冲突字段）
                List<Object[]> processedDataList = testDataList;
                if (!filteredConflictFields.isEmpty()) {
                    System.out.println("[INFO] 表 " + tableName + " 存在冲突字段，开始处理冲突数据...");
                    processedDataList = processConflictData(originalDataList, filteredConflictFields, tableConfig);
                }

                // ✅ 构建 INSERT SQL ---
                String insertSql = buildInsertSql(tableName, filteredFieldNames,filteredConflictFields);

                // ✅ 调用批量插入方法（冲突数据已处理）
                insertDataWithBatchOptimization(
                        tableConfig, conn, tableName, insertSql, filteredFieldNames, processedDataList
                );
            }

            conn.commit(); // 所有表处理完毕，提交事务
            System.out.println("\n✅ 所有表处理完成！");

        } catch (Exception e) {
            try {
                if (conn != null) conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            if (conn != null) {
                try {
                    conn.setAutoCommit(true);
                    conn.close();
                } catch (SQLException ignored) {}
            }
        }
    }

    /**
     * 判断给定的 typeDesc 是否为词库类型（只要 LEXICON_MAP 中存在该 key，就认为是词库类型）
     */
    private static boolean isLexiconType(String typeDesc) {
        return typeDesc != null && LEXICON_MAP.containsKey(typeDesc.trim());
    }

    // ================================
    // 【新增】初始化默认词库（当外置配置不存在时）
    // ================================
    private static void initDefaultLexicon() {
        LEXICON_MAP.put("gender", Arrays.asList("男", "女"));
        LEXICON_MAP.put("country", Arrays.asList("中国", "美国", "日本", "英国", "德国", "法国"));
        LEXICON_MAP.put("chinese_name", Arrays.asList(
                "张三", "李四", "王五", "赵六", "陈七", "周八", "吴九", "郑十",
                "刘一", "陈二", "张三", "李四", "王五", "赵六", "钱七", "孙八"
        ));
        LEXICON_MAP.put("chinese_text", Arrays.asList(
                "这是一个测试", "随机生成的中文内容", "数据生成器工作正常", "今天天气不错", "测试数据用例"
        ));
        LEXICON_MAP.put("english_name", Arrays.asList(
                "John", "Alice", "Bob", "Eve", "Mike", "Anna", "David", "Sophia"
        ));
        LEXICON_MAP.put("english_word", Arrays.asList(
                "Hello", "World", "Test", "Data", "Generator", "Java", "SQL", "JDBC"
        ));
        LEXICON_MAP.put("email_domain", Arrays.asList(
                "gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "163.com", "qq.com"
        ));
        LEXICON_MAP.put("mobile_prefix", Arrays.asList(
                "130", "131", "132", "133", "134", "135", "136", "137", "138", "139"
        ));
        LEXICON_MAP.put("address_keyword", Arrays.asList(
                "北京市海淀区中关村大街", "上海市浦东新区陆家嘴", "广州市天河区珠江新城",
                "深圳市南山区科技园", "成都市武侯区天府软件园"
        ));
        LEXICON_MAP.put("url", Arrays.asList(
                "https://www.example.com", "http://test.com/home", "https://www.baidu.com",
                "http://www.qq.com", "https://github.com"
        ));
        LEXICON_MAP.put("product_type", Arrays.asList(
                "电子产品", "服装", "食品", "家居", "美妆", "图书", "运动器材"
        ));
    }


    /**
     * 根据多字段组合冲突，修复冲突数据
     * 只有当多个冲突字段的组合值重复时，才触发修复，并为组合生成新值
     */
    private static List<Object[]> processConflictData(
            List<Object[]> originalDataList,
            List<String> conflictFields,
            TableConfig tableConfig
    ) {
        List<Object[]> processedData = new ArrayList<>(originalDataList);

        if (conflictFields.isEmpty()) {
            return processedData;
        }

        // 🎯 1. 构建每行冲突字段的组合键，并统计出现次数
        Map<List<Object>, Integer> combinationCountMap = new HashMap<>();
        Map<List<Object>, List<Object[]>> combinationRowsMap = new HashMap<>();

        for (Object[] row : originalDataList) {
            // 构建当前行的冲突字段值组合
            List<Object> combinationKey = new ArrayList<>();
            for (String fieldName : conflictFields) {
                int idx = getFieldIndex(tableConfig, fieldName);
                if (idx >= 0 && idx < row.length) {
                    combinationKey.add(row[idx]);
                } else {
                    combinationKey.add(null); // 字段不存在时用null占位
                }
            }

            // 统计该组合出现的次数
            combinationCountMap.put(combinationKey, combinationCountMap.getOrDefault(combinationKey, 0) + 1);

            // 记录哪些行拥有这个组合（用于后续修复）
            combinationRowsMap.computeIfAbsent(combinationKey, k -> new ArrayList<>()).add(row);
        }

        // 🎯 2. 遍历所有组合，只处理重复的组合（出现次数 > 1）
        for (Map.Entry<List<Object>, Integer> entry : combinationCountMap.entrySet()) {
            List<Object> combinationKey = entry.getKey();
            int count = entry.getValue();

            if (count <= 1) {
                continue; // 唯一，无需修复
            }

            // 获取所有拥有此组合的行
            List<Object[]> rowsWithSameCombination = combinationRowsMap.get(combinationKey);

            // 为这些行中的冲突字段生成新的唯一值（组合级修复）
            for (int rowIndex = 0; rowIndex < rowsWithSameCombination.size(); rowIndex++) {
                Object[] row = rowsWithSameCombination.get(rowIndex);

                // 为每个冲突字段生成新的唯一值（基于类型）
                for (String fieldName : conflictFields) {
                    int fieldIdx = getFieldIndex(tableConfig, fieldName);
                    if (fieldIdx == -1 || fieldIdx >= row.length) {
                        continue;
                    }

                    FieldConfig fieldConfig = findFieldConfig(tableConfig, fieldName);
                    String typeDesc = (fieldConfig != null) ? fieldConfig.typeDesc : "字符串";

                    // 🆕 关键修改：为组合冲突的字段生成新的唯一值，基于类型自增或拼接
                    Object newValue = resolveConflictValueByType(row[fieldIdx], typeDesc, rowIndex + 1);
                    //记录旧值
                    Object oldValue = row[fieldIdx];
                    row[fieldIdx] = newValue;

                    System.out.println("[COMBO-FIX] 组合冲突字段 '" + fieldName + "'，原值: " + oldValue + " → 新值: " + newValue);
                }
            }
        }

        return processedData;
    }
    /**
     * 根据字段的 typeDesc（如 "整数"、"字符串"、"时间" 等），
     * 选择对应的冲突处理策略，返回冲突后的新字段值。
     *
     * @param originalValue 原始字段值（可能为数字、字符串、日期、时间戳等）
     * @param typeDesc      字段类型描述，如 "整数"、"字符串"、"时间"、"小数" 等（来自 FieldConfig.typeDesc）
     * @param suffix        当前冲突后缀计数，如 1, 2, 3... 用于生成 _1, _2 或数值递增
     * @return 冲突处理后的新值，可用于直接插入数据库
     */
    private static Object resolveConflictValueByType(Object originalValue, String typeDesc, int suffix) {
        if (originalValue == null) {
            return null;
        }

        try {
            String lowerType = typeDesc.toLowerCase().trim();

            // ----------------------------------------------------------------------
            // 1. 字符串类型（普通，非时间、非数字）
            //    处理方式：拼接后缀，如 abc → abc_1
            // ----------------------------------------------------------------------
            if (lowerType.contains("字符串") || lowerType.contains("varchar") || lowerType.contains("char")) {
                return getValueWhenStringOrLexicon(originalValue, suffix);
            }
            // ----------------------------------------------------------------------
            // 2. 整数类型（int, integer, long, bigint）
            //    处理方式：数值 + suffix，如 100 → 100 + suffix
            // ----------------------------------------------------------------------
            else if (lowerType.contains("整数") || lowerType.contains("int") || lowerType.contains("integer") ||
                    lowerType.contains("long") || lowerType.contains("bigint")) {
                if (originalValue instanceof Number) {
                    return ((Number) originalValue).intValue() + suffix;
                }
                return Integer.parseInt(originalValue.toString()) + suffix;

            }
            // ----------------------------------------------------------------------
            // 3. 小数 / 浮点类型（double, float, decimal, numeric）
            //    处理方式：数值 + (0.01 * suffix)，如 3.14 → 3.14 + (0.01 * suffix)
            // ----------------------------------------------------------------------
            else if (lowerType.contains("小数") || lowerType.contains("double") || lowerType.contains("float") ||
                    lowerType.contains("decimal") || lowerType.contains("numeric")) {
                if (originalValue instanceof Number) {
                    double val = ((Number) originalValue).doubleValue();
                    return val + (0.01d * suffix); // 可改为 +1 如果希望整数步进
                }
                double val = Double.parseDouble(originalValue.toString());
                return val + (0.01d * suffix);

            }
            // ----------------------------------------------------------------------
            // 4. 时间类型（支持多种格式与时间戳）
            //    typeDesc 包含：时间 / date / timestamp / datetime
            //    处理方式：解析为时间对象，然后 + suffix 秒（或其它时间单位）
            // ----------------------------------------------------------------------
            else if (lowerType.contains("时间") ||lowerType.contains("日期") || lowerType.contains("date") || lowerType.contains("timestamp") || lowerType.contains("datetime")) {
                if (originalValue instanceof java.util.Date) {
                    java.util.Date date = (java.util.Date) originalValue;
                    java.util.Date newDate = new java.util.Date(date.getTime() + (1000 * suffix)); // 1000ms * suffix = suffix秒
                    return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(newDate);
                } else if (originalValue instanceof java.sql.Timestamp) {
                    java.sql.Timestamp ts = (java.sql.Timestamp) originalValue;
                    java.sql.Timestamp newTs = new java.sql.Timestamp(ts.getTime() + (1000 * suffix));
                    return new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(newTs);
                } else {
                    // 尝试解析为字符串形式的时间
                    String strVal = originalValue.toString();

                    // ---- 1. yyyy-MM-dd HH:mm:ss ----
                    if (strVal.matches("^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}$")) {
                        try {
                            LocalDateTime time = LocalDateTime.parse(strVal, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                            time = time.plusSeconds(suffix); // 直接加 suffix 秒
                            return time.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                        } catch (Exception e) { /* ignore */ }
                    }
                    // ---- 2. yyyy-MM-dd ----
                    else if (strVal.matches("^\\d{4}-\\d{2}-\\d{2}$")) {
                        try {
                            LocalDate date = LocalDate.parse(strVal, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                            date = date.plusDays(suffix); // 直接加 suffix 天
                            return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
                        } catch (Exception e) { /* ignore */ }
                    }
                    // 其它情况使用字符串后缀
                    return strVal + "_" + suffix;
                }

            }
            // ----------------------------------------------------------------------
            // 5. 其它类型：Boolean、Enum、未知类型
            //    处理方式：拼接后缀 _suffix，或原值保持（并打印警告）
            // ----------------------------------------------------------------------
            else {
                // 其他情况包括词库也采用和字符串相同的逻辑
                return getValueWhenStringOrLexicon(originalValue, suffix);
            }

        } catch (Exception e) {
            System.err.println("[ERROR] 处理字段类型 '" + typeDesc + "' 的冲突值时出错，原值: " + originalValue + "，错误: " + e.getMessage());
            return originalValue; // 出错时保持原值
        }
    }

    private static String getValueWhenStringOrLexicon(Object originalValue, int suffix) {
        String strVal = originalValue.toString();

        // 检查是否已有后缀，如果有则提取基础值和现有后缀
        int underscore = strVal.lastIndexOf('_');
        if (underscore > 0) {
            try {
                // 尝试解析后缀部分是否为数字
                String suffixStr = strVal.substring(underscore + 1);
                int oriValue = Integer.parseInt(suffixStr);
                suffix +=oriValue;
                // 如果后缀是数字，我们在此基础上递增
                String base = strVal.substring(0, underscore);
                return base + "_" + suffix;
            } catch (NumberFormatException e) {
                // 后缀不是数字，直接添加新后缀
            }
        }
        // 没有合适的后缀或后缀不是数字，直接添加新后缀
        return strVal + "_" + suffix;
    }

    // ================================
    // 辅助方法：获取字段在配置中的索引
    // ================================
    private static int getFieldIndex(TableConfig config, String fieldName) {
        for (int i = 0; i < config.fields.size(); i++) {
            if (config.fields.get(i).fieldName.equals(fieldName)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 【优化】批量插入方法（分批次插入，每批 BATCH_INSERT_SIZE 条）
     */
    private static void insertDataWithBatchOptimization(
            TableConfig config,
            Connection conn,
            String tableName,
            String insertSql,
            List<String> columns,
            List<Object[]> dataList
    ) {
        final int batchSize = getDynamicBatchSize(columns.size()); // 每批插入大小（来自类常量）
        int totalDataSize = dataList.size();
        int totalSuccess = 0;
        int totalAttempt = 0;
        int currentBatchStart = 0;

        // 冲突字段最大值追踪器（按批次独立？或全局？根据需求调整，此处示例全局）
        Map<String, ConflictFieldMax> conflictMaxMap = new HashMap<>();
        for (String fieldName : config.conflictFields) {
            FieldConfig fc = findFieldConfig(config, fieldName);
            String typeDesc = (fc != null) ? fc.typeDesc : "字符串";
            conflictMaxMap.put(fieldName, new ConflictFieldMax(fieldName, typeDesc));
        }
        //区分currentBatchFailed，都是用作记录日志
        List<Object[]> currentBatchFailedShow = new ArrayList<>();

        while (currentBatchStart < totalDataSize) {
            // 1. 截取当前批次数据（分块）
            int currentBatchEnd = Math.min(currentBatchStart + batchSize, totalDataSize);
            List<Object[]> currentBatchData = dataList.subList(currentBatchStart, currentBatchEnd);
            currentBatchStart = currentBatchEnd;

            System.out.println(String.format("[INFO] 处理批次 [%d-%d/%d]，待插入条数: %d",
                   currentBatchStart<batchSize?1:currentBatchStart - batchSize + 1, currentBatchStart, totalDataSize, currentBatchData.size()));

            // 2. 当前批次重试逻辑（独立重试）
            int batchMaxRetry = config.maxRetryTimes;
            int batchAttempt = 0;
            List<Object[]> batchFailedRows = new ArrayList<>(currentBatchData); // 初始为全量当前批次数据

            while (batchAttempt <= batchMaxRetry && !batchFailedRows.isEmpty()) {
                batchAttempt++;
                System.out.println(String.format("[INFO] 批次 [%d-%d] 第 %d 次尝试插入，待插入条数: %d",
                       currentBatchStart<batchSize?1:currentBatchStart - batchSize + 1, currentBatchStart, batchAttempt, batchFailedRows.size()));

                try (PreparedStatement ps = conn.prepareStatement(insertSql)) {
                    int[] batchResults = new int[0];
                    List<Object[]> currentBatchFailed = new ArrayList<>();

                    // a. 填充当前批次数据到 PreparedStatement
                    for (Object[] row : batchFailedRows) {
                        for (int j = 0; j < row.length; j++) {
                            ps.setObject(j + 1, row[j]);
                        }
                        ps.addBatch();
                    }

                    // b. 执行当前批次插入
                    batchResults = ps.executeBatch();

                    // c. 分析当前批次插入结果
                    for (int i = 0; i < batchResults.length; i++) {
                        int result = batchResults[i];
                        if (result == 1 || result == Statement.SUCCESS_NO_INFO) {
                            totalSuccess++; // 全局成功计数
                            Object[] row = batchFailedRows.get(i);
                            // 更新全局冲突字段最大值（如果需要）
                            for (String fieldName : config.conflictFields) {
                                int idx = getFieldIndex(config, fieldName);
                                if (idx >= 0 && idx < row.length) {
                                    Object value = row[idx];
                                    updateConflictMax(conflictMaxMap, fieldName, value);
                                }
                            }
                        } else {
                            //这两个变量不能合并，否则日志打印会错误
                            currentBatchFailed.add(batchFailedRows.get(i)); // 记录当前批次失败行
                            currentBatchFailedShow.add(batchFailedRows.get(i)); // 记录当前批次失败行
                        }
                    }

                    System.out.println(String.format("[INFO] 批次 [%d-%d] 第 %d 次尝试：成功 %d 条，失败 %d 条",
                           currentBatchStart<batchSize?1:currentBatchStart - batchSize + 1, currentBatchStart, batchAttempt,
                            batchFailedRows.size() - currentBatchFailed.size(), currentBatchFailed.size()));

                    // d. 当前批次全部成功则退出重试
                    if (currentBatchFailed.isEmpty()) {
                        break;
                    }

                    // e. 达到最大重试次数则终止当前批次
                    if (batchAttempt >= batchMaxRetry) {
                        System.out.println(String.format("[WARN] 批次 [%d-%d] 达到最大重试次数 %d，停止重试。剩余失败 %d 条。",
                               currentBatchStart<batchSize?1:currentBatchStart - batchSize + 1, currentBatchStart, batchMaxRetry, currentBatchFailed.size()));
                        break;
                    }

                    // f. 修复当前批次失败行的冲突字段，准备重试
                    batchFailedRows = repairBatchFailedRows(currentBatchFailed, config, batchAttempt);

                } catch (SQLException e) {
                    System.out.println(String.format("[WARN] 批次 [%d-%d] 第 %d 次尝试插入异常: %s",
                           currentBatchStart<batchSize?1:currentBatchStart - batchSize + 1, currentBatchStart, batchAttempt, e.getMessage()));
                    e.printStackTrace();
                    if (batchAttempt >= batchMaxRetry) {
                        System.out.println("[INFO] 批次达到最大重试次数，停止重试。");
                        break;
                    }
                    // 异常时保留当前批次失败数据继续重试
                }
            }

            int finalFailedCount = currentBatchFailedShow.size();
            int finalSuccessCount = currentBatchData.size() - finalFailedCount;

            System.out.println(String.format("[SUMMARY] 批次 [%d-%d] 最终成功插入 %d 条，失败 %d 条",
                   currentBatchStart<batchSize?1:currentBatchStart - batchSize + 1, currentBatchStart,
                    finalSuccessCount, finalFailedCount));
        }

        System.out.println(String.format("[SUMMARY] 表 %s 全量插入完成！总成功: %d 条，总失败: %d 条",
                tableName, totalSuccess, totalDataSize - totalSuccess));
    }

    /**
     * 修复当前批次内插入失败的行（仅修复当前批次数据）
     */
    private static List<Object[]> repairBatchFailedRows(List<Object[]> failedRows, TableConfig config, int attempt) {
        List<Object[]> repaired = new ArrayList<>();

        // 创建当前批次冲突字段最大值追踪器（独立于全局，避免跨批次干扰）
        Map<String, ConflictFieldMax> batchConflictMaxMap = new HashMap<>();
        for (String fieldName : config.conflictFields) {
            FieldConfig fc = findFieldConfig(config, fieldName);
            String typeDesc = (fc != null) ? fc.typeDesc : "字符串";
            batchConflictMaxMap.put(fieldName, new ConflictFieldMax(fieldName, typeDesc));
        }

        // 分析失败行中的冲突字段当前值，更新当前批次最大值追踪器
        for (Object[] row : failedRows) {
            for (String fieldName : config.conflictFields) {
                int idx = getFieldIndex(config, fieldName);
                if (idx >= 0 && idx < row.length) {
                    Object value = row[idx];
                    updateConflictMax(batchConflictMaxMap, fieldName, value); // 复用原更新逻辑
                }
            }
        }

        // 修复当前批次失败行的冲突字段
        for (Object[] row : failedRows) {
            Object[] newRow = Arrays.copyOf(row, row.length);
            for (String fieldName : config.conflictFields) {
                int idx = getFieldIndex(config, fieldName);
                if (idx >= 0 && idx < newRow.length) {
                    Object oldValue = newRow[idx];
                    FieldConfig fieldCfg = findFieldConfig(config, fieldName);
                    String typeDesc = (fieldCfg != null) ? fieldCfg.typeDesc : "字符串";

                    // 修复时使用当前批次尝试次数（attempt）作为后缀计数
                    Object newValue = resolveConflictValueByType(oldValue, typeDesc, attempt);

                    newRow[idx] = newValue;
                    System.out.println("[REPAIR] 批次内修复字段 '" + fieldName + "' 的冲突值，原值: " + oldValue + " → 新值: " + newValue);
                }
            }
            repaired.add(newRow);
        }
        System.out.println("[INFO] 当前批次已修复 " + repaired.size() + " 行冲突字段数据，准备重试...");
        return repaired;
    }

    /**
     * 根据字段名与字段值，更新该冲突字段的最大值记录
     */
    private static void updateConflictMax(Map<String, ConflictFieldMax> conflictMaxMap, String fieldName, Object value) {
        if (value == null) {
            return;
        }

        ConflictFieldMax tracker = conflictMaxMap.get(fieldName);
        if (tracker == null) {
            return;
        }

        String typeDesc = tracker.typeDesc; // 可能为 null

        try {
            // 默认类型为字符串（安全兜底）
            String effectiveTypeDesc = (typeDesc != null) ? typeDesc.trim() : "字符串";
            String lowerType = effectiveTypeDesc.toLowerCase();

            if (lowerType.contains("字符串") || lowerType.contains("varchar") || lowerType.contains("char")) {
                // 类型为字符串：尝试拼接后缀逻辑
                String strVal = value.toString();
                updateStringValueLogic(tracker, strVal);

            } else if (lowerType.contains("整数") || lowerType.contains("int") || lowerType.contains("integer")) {
                // 类型为整数：取数值并比较
                int currentValue;
                if (value instanceof Number) {
                    currentValue = ((Number) value).intValue();
                } else {
                    currentValue = Integer.parseInt(value.toString());
                }
                if (tracker.intValue == null || currentValue > tracker.intValue) {
                    tracker.intValue = currentValue;
                }

            } else if (lowerType.contains("小数") || lowerType.contains("double") || lowerType.contains("float")
                    || lowerType.contains("decimal") || lowerType.contains("numeric")) {
                // 类型为小数：取 double 值并比较
                double currentValue;
                if (value instanceof Number) {
                    currentValue = ((Number) value).doubleValue();
                } else {
                    currentValue = Double.parseDouble(value.toString());
                }
                if (tracker.doubleValue == null || currentValue > tracker.doubleValue) {
                    tracker.doubleValue = currentValue;
                }

            } else if (lowerType.contains("时间") ||lowerType.contains("日期") || lowerType.contains("date") || lowerType.contains("timestamp") || lowerType.contains("datetime")) {
                // 类型为时间：解析并比较
                LocalDateTime dt = null;
                if (value instanceof java.util.Date) {
                    dt = ((java.util.Date) value).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                } else if (value instanceof java.sql.Timestamp) {
                    dt = ((java.sql.Timestamp) value).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
                } else if (value instanceof LocalDateTime) {
                    dt = (LocalDateTime) value;
                } else {
                    // 无法识别为时间类型，按字符串处理
                    String strVal = value.toString();
                    updateStringValueLogic(tracker, strVal);
                    return;
                }

                if (dt != null && (tracker.dateTimeValue == null || dt.isAfter(tracker.dateTimeValue))) {
                    tracker.dateTimeValue = dt;
                }

            } else {
                // 未知类型：默认按字符串处理（安全兜底）
                String strVal = value.toString();
                updateStringValueLogic(tracker, strVal);
            }

        } catch (Exception e) {
            // 增强日志：打印异常类型 + 消息 + 字段信息，便于排查
            System.err.println("[WARN] 更新冲突字段最大值失败，字段: " + fieldName + "，值: " + value + "，类型描述: " + tracker.typeDesc);
            System.err.println("[WARN] 错误详情: " + e.getClass().getSimpleName() + " - " + e.getMessage());
            e.printStackTrace(); // 开发阶段保留堆栈，生产可注释掉
        }
    }

    /**
     * 根据插入失败的行，修复其冲突字段的数据，然后准备重试
     */
    private static List<Object[]> repairFailedRows(List<Object[]> failedRows, TableConfig config, int totalAttempt) {
        List<Object[]> repaired = new ArrayList<>();

        // 创建冲突字段最大值追踪器
        Map<String, ConflictFieldMax> conflictMaxMap = new HashMap<>();
        for (String fieldName : config.conflictFields) {
            FieldConfig fc = findFieldConfig(config, fieldName);
            String typeDesc = (fc != null) ? fc.typeDesc : "字符串";
            conflictMaxMap.put(fieldName, new ConflictFieldMax(fieldName, typeDesc));
        }

        // 分析失败行中的冲突字段当前值，更新最大值追踪器
        for (Object[] row : failedRows) {
            for (String fieldName : config.conflictFields) {
                int idx = getFieldIndex(config, fieldName);
                if (idx >= 0 && idx < row.length) {
                    Object value = row[idx];
                    updateConflictMax(conflictMaxMap, fieldName, value);
                }
            }
        }

        for (Object[] row : failedRows) {
            Object[] newRow = Arrays.copyOf(row, row.length);
            for (String fieldName : config.conflictFields) {
                int idx = getFieldIndex(config, fieldName);
                if (idx >= 0 && idx < newRow.length) {
                    Object oldValue = newRow[idx];
                    FieldConfig fieldCfg = findFieldConfig(config, fieldName);
                    String typeDesc = (fieldCfg != null) ? fieldCfg.typeDesc : "字符串";

                    Object newValue = resolveConflictValueByType(oldValue, typeDesc, totalAttempt+1);

                    newRow[idx] = newValue;
                    System.out.println("[REPAIR] 修复字段 '" + fieldName + "' 的冲突值，原值: " + oldValue + " → 新值: " + newValue);
                }
            }
            repaired.add(newRow);
        }
        System.out.println("[INFO] 已修复 " + repaired.size() + " 行冲突字段数据，准备重试...");
        return repaired;
    }

    /**
     * 从配置文件的行列表中解析出当前表配置的 max_retry_times 值
     */
    private static int parseMaxRetryTimesFromConfig(List<String> lines) {
        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty() || line.startsWith("#")) continue;

            if (line.startsWith("max_retry_times:")) {
                String valueStr = line.substring("max_retry_times:".length()).trim();
                try {
                    return Integer.parseInt(valueStr);
                } catch (NumberFormatException e) {
                    System.out.println("[WARN] 解析最大重试次数失败，配置为: " + valueStr + "，使用默认值 3");
                    return 3; // 默认值
                }
            }
        }
        return 3; // 默认重试次数
    }

    /**
     * 更新冲突字段的字符串类型相关最大值信息（包括 基础值_数字 后缀 和 普通字符串最大值逻辑）
     * 该方法供字符串类型分支 和 else 兜底分支复用
     */
    private static void updateStringValueLogic(ConflictFieldMax tracker, String strVal) {
        int underscore = strVal.lastIndexOf('_');
        if (underscore > 0) {
            String base = strVal.substring(0, underscore);
            String suffixStr = strVal.substring(underscore + 1);
            try {
                int suffix = Integer.parseInt(suffixStr);
                if (tracker.baseValue == null) {
                    tracker.baseValue = base;
                } else if (tracker.baseValue.equals(base)) {
                    if (tracker.numericSuffix == null || suffix > tracker.numericSuffix) {
                        tracker.numericSuffix = suffix;
                    }
                }
            } catch (NumberFormatException ignored) {
                // 不是数字后缀，按普通字符串处理
                if (tracker.stringValue == null || strVal.compareTo(tracker.stringValue) > 0) {
                    tracker.stringValue = strVal;
                }
            }
        } else {
            // 没有后缀，按普通字符串处理
            if (tracker.stringValue == null || strVal.compareTo(tracker.stringValue) > 0) {
                tracker.stringValue = strVal;
            }
        }
    }

    // ================================
    // 以下为原有功能方法（未改动，保持原逻辑）
    // ================================

    // 获取数据库连接（自动加载对应数据库驱动）
    private static Connection getConnection() throws SQLException, ClassNotFoundException {
        Class.forName(DRIVER_CLASS);
        return DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD);
    }

    // 获取表字段信息：字段名、类型、精度、小数位、是否自增
    private static boolean getFieldNamesAndTypes(Connection conn, String tableName,
                                                 List<String> fieldNames, List<Integer> fieldTypes,
                                                 List<Integer> fieldPrecisions, List<Integer> fieldScales,
                                                 List<Boolean> fieldIsAutoIncrement) throws SQLException {

        // 清空结果集合
        fieldNames.clear();
        fieldTypes.clear();
        fieldPrecisions.clear();
        fieldScales.clear();
        fieldIsAutoIncrement.clear();

        // ✅ 使用 DatabaseMetaData 获取表的列（字段）信息，无需表中有数据！
        DatabaseMetaData metaData = conn.getMetaData();
        Set<String> processedFieldNames = new HashSet<>(); // 用于去重

        // 参数说明：
        // catalog: 通常为 null（除非你的数据库有catalog概念，比如Oracle）
        // schemaPattern: 通常为 null（或者填你的schema，比如MySQL一般填 null）
        // tableNamePattern: 传入你的表名，如 "user" 或 "t_user"
        // columnNamePattern: null 表示查询所有列
        try (ResultSet columns = metaData.getColumns(null, null, tableName, null)) {
            while (columns.next()) {
                String columnName = columns.getString("COLUMN_NAME");
                int columnType = columns.getInt("DATA_TYPE");
                int precision = columns.getInt("COLUMN_SIZE");
                int scale = columns.getInt("DECIMAL_DIGITS");
                boolean isAutoIncrement = false; // 默认非自增

                // ✅ 去重判断：如果字段名已经处理过，则跳过
                if (processedFieldNames.contains(columnName)) {
                    System.out.println("[WARN] 发现重复字段（已跳过）: " + columnName);
                    continue;
                }

                // 记录已处理的字段名
                processedFieldNames.add(columnName);

                // 添加到结果集合
                fieldNames.add(columnName);
                fieldTypes.add(columnType);
                fieldPrecisions.add(precision);
                fieldScales.add(scale);
                fieldIsAutoIncrement.add(isAutoIncrement);
            }

            // 如果一个字段都没拿到，说明表可能不存在
            if (fieldNames.isEmpty()) {
                System.out.println("[WARN] 表 '" + tableName + "' 不存在或无字段，无法获取字段信息。");
                return false;
            }

            // 成功获取字段信息
            return true;
        }
    }

    /**
     * 获取表的主键字段 + 唯一键字段（自动去重）
     *
     * @param conn      数据库连接
     * @param tableName 表名
     * @return 不重复的主键 + 唯一键字段名称列表，顺序一般是主键在前，唯一键在后
     * @throws SQLException
     */
    private static List<String> getTablePrimaryKeyAndUniqueFields(Connection conn, String tableName) throws SQLException {
        // 使用 LinkedHashSet 保证字段唯一且大致保留顺序（主键 -> 唯一键）
        Set<String> keyFieldsSet = new LinkedHashSet<>();

        DatabaseMetaData metaData = conn.getMetaData();

        // --- 1. 获取主键字段 ---
        try (ResultSet pkRs = metaData.getPrimaryKeys(null, null, tableName)) {
            while (pkRs.next()) {
                String columnName = pkRs.getString("COLUMN_NAME");
                if (columnName != null) {
                    keyFieldsSet.add(columnName); // 自动去重
                }
            }
        }

        // --- 2. 获取唯一键字段（排除主键，但 Set 会自动去重，所以无需再手动判断）---
        try (ResultSet indexRs = metaData.getIndexInfo(null, null, tableName, true, false)) {
            while (indexRs.next()) {
                boolean isUnique = !indexRs.getBoolean("NON_UNIQUE"); // NON_UNIQUE=false 表示是唯一索引
                if (isUnique) {
                    String columnName = indexRs.getString("COLUMN_NAME");
                    if (columnName != null) {
                        keyFieldsSet.add(columnName); // 自动去重，包括和主键重复的情况
                    }
                }
            }
        }

        // 返回 List（如果需要保持顺序，LinkedHashSet 已经帮我们排好了）
        return new ArrayList<>(keyFieldsSet);
    }

    // 获取配置文件路径，如：./t_user_config.txt
    private static String getTableConfigFilePath(String tableName) {
        return CONFIG_DIR + tableName + CONFIG_FILE_SUFFIX;
    }

    // 加载已有配置，或为表生成默认配置并保存
    private static TableConfig loadOrCreateTableConfig(Connection conn, String tableName, String configFile,
                                                       List<String> dbFieldNames, List<Integer> dbFieldTypes,
                                                       List<Integer> dbFieldPrecisions, List<Integer> dbFieldScales,
                                                       List<String> autoConflictFields) throws SQLException {
        TableConfig config = new TableConfig();
        config.tableName = tableName;
        config.conflictFields.addAll(autoConflictFields); // 自动填充冲突字段（主键/唯一键）

        File file = new File(configFile);
        if (file.exists()) {
            System.out.println("[INFO] 读取用户配置文件: " + configFile);
            List<String> lines = readFileLines(configFile);
            parseConfigLines(lines, config, dbFieldNames);
        } else {
            System.out.println("[INFO] 未找到配置文件，为表 " + tableName + " 生成默认配置并保存。");
            generateDefaultConfig(config, dbFieldNames, dbFieldTypes, dbFieldPrecisions, dbFieldScales);
            saveConfig(configFile, config);

            System.out.println("[INFO] 配置文件已生成，路径为： " + new File(configFile).getAbsolutePath());
            System.out.println("[INFO] 【当前表 " + tableName + " 暂无配置，跳过数据生成。请按需修改配置后，重新运行程序以生成该表数据。】");
            return null;
        }
        return config;
    }

    // 读取配置文件内容
    private static List<String> readFileLines(String path) {
        List<String> lines = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            String line;
            while ((line = reader.readLine()) != null) {
                lines.add(line);
            }
        } catch (IOException e) {
            System.out.println("[错误] 读取配置文件失败: " + e.getMessage());
        }
        return lines;
    }

    // 解析配置文件中的每一行
    private static void parseConfigLines(List<String> lines, TableConfig config, List<String> dbFieldNames) {
        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty() || line.startsWith("#")) continue;

            // 解析冲突字段配置（格式：# 冲突字段: id,email,code）
            if (line.startsWith("# 冲突字段:")) {
                String conflictLine = line.substring("# 冲突字段:".length()).trim();
                if (!conflictLine.isEmpty()) {
                    String[] fields = conflictLine.split(":")[0].trim().split(",");
                    for (String f : fields) {
                        if (!f.trim().isEmpty() && dbFieldNames.contains(f.trim())) {
                            config.conflictFields.add(f.trim());
                        }
                    }
                }
                continue;
            }

            // 解析字段配置（格式：字段名 : typeDesc : 长度 : 随机值）
            String[] parts = line.split(" : ");
            if (parts.length < 4) {
                System.out.println("[警告] 配置格式错误，跳过行: " + line);
                continue;
            }

            String fieldName = parts[0].trim();
            String typeDesc = parts[1].trim();
            String lengthStr = parts[2].trim();
            String randomValues = parts[3].trim();

            // 解析长度（可选）
            Integer length = null;
            if (!"空".equals(lengthStr) && !lengthStr.isEmpty()) {
                try {
                    length = Integer.parseInt(lengthStr);
                } catch (NumberFormatException ignored) {}
            }

            // 解析随机值（处理默认值格式：默认 001）
            String actualRandomValue = randomValues;
            if (randomValues != null && randomValues.startsWith("默认 ")) {
                actualRandomValue = randomValues.trim();
            }

            // 仅处理数据库存在的字段
            if (dbFieldNames.contains(fieldName)) {
                // ✅ 新增：根据 typeDesc 判断是否为词库类型，如果是，则 isLexiconField=true
                boolean isLexicon = isLexiconType(typeDesc);
                config.addField(new FieldConfig(fieldName, typeDesc, length, null, actualRandomValue, isLexicon));
            }
            config.maxRetryTimes = parseMaxRetryTimesFromConfig(lines);

        }
    }

    // 为表生成默认配置
    private static void generateDefaultConfig(TableConfig config, List<String> dbFieldNames, List<Integer> dbFieldTypes,
                                              List<Integer> dbFieldPrecisions, List<Integer> dbFieldScales) {
        for (int i = 0; i < dbFieldNames.size(); i++) {
            String fieldName = dbFieldNames.get(i);
            int jdbcType = dbFieldTypes.get(i);
            int precision = dbFieldPrecisions.get(i);
            int scale = dbFieldScales.get(i);

            String typeDesc = SQL_TYPE_TO_DESC.getOrDefault(jdbcType, "字符串");

            // 特殊处理小数类型
            if (jdbcType == Types.DECIMAL || jdbcType == Types.NUMERIC) {
                typeDesc = "小数";
            }

            // 长度/精度处理
            Integer length = (precision > 0) ? precision : null;
            Integer fieldScale = (scale >= 0) ? scale : null;

            // 默认随机值为 "随机"
            boolean isLexicon = isLexiconType(typeDesc); // 只要词库中存在该类型，就启用词库
            config.addField(new FieldConfig(fieldName, typeDesc, length, fieldScale, "随机",isLexicon));
        }
    }

    // 保存配置到文件
    private static void saveConfig(String path, TableConfig config) {
        StringBuilder sb = new StringBuilder();

        // 写入配置文件头（含冲突字段说明）
        sb.append("# =============================================\n");
        sb.append("# 表字段测试数据配置文件\n");
        sb.append("# 说明：每行配置一个字段，格式为：字段名 : typeDesc : 长度 : 随机规则\n");
        sb.append("# 注意：使用中文冒号分隔，空行或以#开头的行将被忽略\n");
        sb.append("#\n");
        sb.append("# typeDesc可选值说明：\n");
        sb.append("#   名字    ：生成常见中文姓名（如：张三、李四）\n");
        sb.append("#   中文    ：生成随机中文内容（如：这是一个测试）\n");
        sb.append("#   英文    ：生成随机英文内容（如：Hello、Tom）\n");
        sb.append("#   邮箱    ：生成随机邮箱（如：user@example.com）\n");
        sb.append("#   手机    ：生成随机中国大陆手机号（如：13812345678）\n");
        sb.append("#   地址    ：生成随机地址（如：北京市海淀区中关村大街1号）\n");
        sb.append("#   网址    ：生成随机网址（如：https://example.com）\n");
        sb.append("#   性别    ：生成随机性别（如：男、女）\n");
        sb.append("#   国家    ：生成随机国家（如：中国、美国）\n");
        sb.append("#   货币    ：生成随机货币（如：人民币、美元）\n");
        sb.append("#   产品    ：生成随机产品（如：服装、食品）\n");
        sb.append("#   身份证   ：生成随机身份证（包括大陆和港澳台）\n");
        sb.append("#   日期    ：生成随机日期（如：2025-09-13、2025-09-14）\n");
        sb.append("#   时间    ：生成随机时间（如：2009-04-09 15:29:49、2023-07-13 07:16:16）\n");
        sb.append("#   定制    ：生成随机固定值（如：001、admin）\n");
        sb.append("#   IP      ：生成随机IPv4或IPv6（如：192.168.1.1）（忽略大小写）\n");
        sb.append("#   IP4     ：生成随机IPv4（忽略大小写）\n");
        sb.append("#   IP6     ：生成随机IPv6（忽略大小写）\n");
        sb.append("#   范围    ：生成范围内随机值\n");
        sb.append("#   拼接    ：生成拼接后的前缀+随机值+后缀(可选)\n");
        sb.append("# - 冲突字段: 自动识别的主键/唯一键字段（无需手动配置）\n");
        sb.append("#\n");
        sb.append("# 长度：字段存储长度（仅对字符串、整数、小数有效，如VARCHAR(20)填20，留空表示采用代码中默认值）\n");
        sb.append("# 随机规则：若typeDesc为\"定制\"，随机规则处填写具体值。用逗号分隔（如：男,女|飞机，坦克,|中国,）\n");
        sb.append("# 随机规则：若typeDesc为\"范围\"，随机规则处填写具体值。要加左右括号（如：(-10，100)|(2000-01-01，2050-12-31)|(-100.15,100.25)|(2000-01-01 14:30:00，2050-12-31 23:59:59)）\n");
        sb.append("# 随机规则：若typeDesc为\"拼接\"，随机规则处填写固定格式：前缀@类型|后缀（如：cn_@中文 或 name_@名字|b 或 int@整数）注意@后的类型必须是除了拼接之外的已存在的类型\n");
        sb.append("# =============================================\n\n");

        // 写入冲突字段（自动识别）
        if (!config.conflictFields.isEmpty()) {
            sb.append("# 冲突字段: ").append(String.join(",", config.conflictFields)).append("\n\n");
        }

        // 写入字段配置
        sb.append("# 表名：").append(config.tableName).append("\n");

        // ==== 新增：写入最大重试次数配置项（默认 3 次） ====
        sb.append("\n# 最大重试次数（当批量插入因冲突失败时，自动重试处理冲突数据的次数）\n");
        sb.append("max_retry_times: 3\n\n");

        sb.append("# 示例: \n");
        sb.append("# id : 整数 : 10 : 随机 \n");
        sb.append("# str : 产品 : 255 : 随机 \n");
        sb.append("# date : 日期 : 10 : 随机 \n");
        sb.append("# name : 定制 : 255 : 001,B，西瓜 \n");
        sb.append("# note : 范围 : 255 : (-1000,1000) \n");
        sb.append("# msg0 : 拼接 : 255 : name_@名字|b \n");
        sb.append("# msg1 : 拼接 : 255 : cn_@定制@a,b,c,d, \n");
        sb.append("# msg2 : 拼接 : 255 : msg_@范围@(2000-01-01 14:30:00，2050-12-31 23:59:59)|end \n\n");

        for (FieldConfig fc : config.fields) {
            sb.append(fc.fieldName).append(" : ")
                    .append(fc.typeDesc).append(" : ")
                    .append(fc.length != null ? fc.length : "空").append(" : ")
                    .append(fc.randomValues).append("\n");
        }


        try (PrintWriter out = new PrintWriter(new FileWriter(path))) {
            out.print(sb.toString());
        } catch (IOException e) {
            System.out.println("[错误] 保存配置文件失败: " + e.getMessage());
        }
    }

    // 校验字段配置，提示未配置的字段
    private static void validateFieldConfig(TableConfig config, List<String> dbFieldNames, String tableName) {
        List<String> configured = new ArrayList<>();
        for (FieldConfig fc : config.fields) configured.add(fc.fieldName);

        List<String> missing = new ArrayList<>();
        for (String dbField : dbFieldNames) {
            if (!configured.contains(dbField)) missing.add(dbField);
        }

        if (!missing.isEmpty()) {
            System.out.println("[警告] 表 " + tableName + " 存在未配置字段，将使用默认随机值生成: " + missing);
        }
    }

    // ================================
    // 【优化】生成测试数据（增强随机策略）
    // ================================
    private static List<Object[]> generateTestDataInDbColumnOrder(TableConfig config, List<String> dbFieldNames, int count) {
        List<Object[]> rows = new ArrayList<>();
        Random random = new Random(System.currentTimeMillis()); // 使用时间戳种子增强随机性

        for (int i = 0; i < count; i++) {
            Object[] values = new Object[dbFieldNames.size()];
            for (int idx = 0; idx < dbFieldNames.size(); idx++) {
                String fieldName = dbFieldNames.get(idx);
                FieldConfig fc = findFieldConfig(config, fieldName);
                Object value = (fc != null) ? generateFieldValue(fc, random, i) : "DEFAULT_UNKNOWN"; // 传入行号i增加多样性
                values[idx] = value;
            }
            rows.add(values);
        }

        return rows;
    }
    // 查找字段配置
    private static FieldConfig findFieldConfig(TableConfig config, String fieldName) {
        for (FieldConfig fc : config.fields) {
            if (fc.fieldName.equals(fieldName)) return fc;
        }
        return null;
    }

    // ================================
    // 【优化】字段值生成（增强随机策略）
    // ================================
    private static Object generateFieldValue(FieldConfig fc, Random random, int rowIdx) {
        String randomVal = fc.randomValues;
        String typeDesc = fc.typeDesc;


        // 词库类型字段优先使用词库
        if (fc.isLexiconField && LEXICON_MAP.containsKey(typeDesc)) {
            List<String> lexicon = LEXICON_MAP.get(typeDesc);
            return lexicon.get(random.nextInt(lexicon.size())); // 基础随机
        }

        // 按类型生成数据（增强随机范围）
        if ("名字".equals(typeDesc)) {
            return generateChineseName(random, rowIdx);
        } else if ("中文".equals(typeDesc)) {
            return generateChineseText(random, rowIdx);
        } else if ("英文".equals(typeDesc)) {
            return generateEnglishContent(random, rowIdx);
        } else if ("邮箱".equals(typeDesc)) {
            return generateEmail(random, rowIdx);
        } else if ("手机".equals(typeDesc)) {
            return generateMobile(random, rowIdx);
        } else if ("地址".equals(typeDesc)) {
            return generateAddress(random, rowIdx);
        } else if ("网址".equals(typeDesc)) {
            return generateUrl(random, rowIdx);
        } else if ("性别".equals(typeDesc)) {
            return generateGender(random);
        } else if ("国家".equals(typeDesc)) {
            return generateCountry(random);
        } else if ("货币".equals(typeDesc)) {
            return generateCurrency(random);
        } else if ("产品".equals(typeDesc)) {
            return generateProductType(random);
        } else if ("身份证".equals(typeDesc) || typeDesc.contains("身份证")){
            return generateIdCard(random);
        } else if (typeDesc.contains("小数") || typeDesc.contains("decimal")) {
            return generateDecimal(random, fc.length, fc.scale);
        } else if (typeDesc.contains("整数") || typeDesc.contains("int")) {
            return generateInteger(random, fc.length, rowIdx);
        } else if (typeDesc.contains("日期") || typeDesc.contains("date")) {
            return generateDate(random, rowIdx);
        }else if (typeDesc.contains("时间") || typeDesc.contains("datetime")) {
            return generateDateTime(random, rowIdx);
        } else if (typeDesc.contains("字符串")) {
            return generateString(random, fc.length, rowIdx);
        } else if (typeDesc.contains("定制")){
            return generateCustom(random,randomVal);
        } else if ("IP".equalsIgnoreCase(typeDesc) || "IP地址".equalsIgnoreCase(typeDesc)) {
            return random.nextBoolean() ? generateIPv4(random) : generateIPv6(random);
        } else if ("IP4".equalsIgnoreCase(typeDesc)) {
            return generateIPv4(random);
        } else if ("IP6".equalsIgnoreCase(typeDesc)) {
            return generateIPv6(random);
        } else if ("范围".equals(typeDesc)){
            return generateRangeFieldValue(randomVal);
        } else if ("拼接".equals(typeDesc)) {
            return generateConcatenatedFieldValue(random, rowIdx, randomVal);
        } else {
            return "DEFAULT_" + typeDesc + "_" + rowIdx;
        }
    }

    /**
     * 生成身份证号码（18位），可能是：
     * - 大陆居民身份证（使用常见地区码，如 110101、440101...）
     * - 港澳台居民居住证（使用虚拟地区码，如 810000、820000、830000）
     *
     * 号码结构：前6位地区码 + 8位出生日期 + 3位顺序码 + 1位校验码
     * 校验码算法：与大陆身份证标准（GB 11643-1999）一致，兼容所有类型
     *
     * @param random 随机数生成器
     * @return 合法的18位身份证或港澳台居住证号码
     */
    private static String generateIdCard(Random random) {
        // ===== 1. 地区码：随机选择 大陆 或 港澳台 =====
        String[] areaCodes;

        // 随机决定是生成大陆身份证，还是港澳台居民居住证
        if (random.nextBoolean()) {
            // 大陆地区码（常用真实或测试地区）
            areaCodes = new String[]{
                    "110101", "110105", "310101", "310104", "440101", "440301",
                    "420101", "320101", "500101", "610101"
                    // 可继续添加更多城市，如 330101（杭州）、120101（天津）等
            };
        } else {
            // 港澳台居民居住证地区码（虚拟，非真实行政地区，但格式合法，用于居住证）
            areaCodes = new String[]{
                    "810000", // 香港
                    "820000", // 澳门
                    "830000"  // 台湾
            };
        }

        // 随机选一个地区码
        String areaCode = areaCodes[random.nextInt(areaCodes.length)];

        // ===== 2. 出生日期：格式 yyyyMMdd，范围：1950~2010（覆盖大部分成人）=====
        int startYear = 1950;
        int endYear = 2010;
        int year = startYear + random.nextInt(endYear - startYear + 1);
        int month = 1 + random.nextInt(12);
        int day = 1 + random.nextInt(28); // 简化：每月最多28天，避免日期越界

        String birthDate = String.format("%04d%02d%02d", year, month, day);

        // ===== 3. 顺序码：3位数字，如 100 ~ 999 =====
        int sequence = 100 + random.nextInt(900); // 保证是三位数
        String id17 = areaCode + birthDate + String.format("%03d", sequence);

        // ===== 4. 校验码：使用标准算法（与大陆身份证一致）=====
        char checkDigit = calculateIdCardCheckDigit(id17);

        // ===== 5. 返回完整18位号码 =====
        return id17 + checkDigit;
    }

    private static char calculateIdCardCheckDigit(String id17) {
        if (id17 == null || id17.length() != 17) {
            throw new IllegalArgumentException("身份证前17位必须是17位数字");
        }

        int[] weightFactors = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
        char[] checkCodes = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

        int sum = 0;
        for (int i = 0; i < 17; i++) {
            char c = id17.charAt(i);
            if (!Character.isDigit(c)) {
                throw new IllegalArgumentException("身份证前17位必须全为数字");
            }
            int digit = c - '0';
            sum += digit * weightFactors[i];
        }

        int mod = sum % 11;
        return checkCodes[mod];
    }

    /**
     * 拼接类型字段值处理器。
     * 用于处理 typeDesc = "拼接" 的情况，支持如下格式的配置：
     * - 基础类型：A@日期|B → 拼接前缀A + 生成的日期值 + 后缀B
     * - 定制类型：A@定制@男,女|B → 拼接前缀A + 调用generateCustom(random, "男,女")返回的随机值 + 后缀B
     * - 范围类型：A@范围@(1,100)|B → 拼接前缀A + 调用generateRangeFieldValue("(1,100)")返回的随机值 + 后缀B
     * - 不允许：A@拼接|B 或 A@拼接@xxx|B，防止递归调用导致栈溢出
     *
     * @param random   随机数生成器，用于生成随机值（传入 generateFieldValue、generateCustom、generateRangeFieldValue 方法）
     * @param rowIdx   当前行号，用于生成上下文相关值（传入 generateFieldValue 方法）
     * @param randomVal 拼接配置字符串，格式为：
     *                  - 基础：前缀@类型|后缀，如 A@日期|B
     *                  - 定制：前缀@定制@配置内容|后缀，如 A@定制@男,女|B
     *                  - 范围：前缀@范围@配置表达式|后缀，如 A@范围@(1,100)|B
     *                  其中：
     *                    - 前缀：要拼接到结果前面的字符串，如 "A"
     *                    - 类型：要生成值的类型描述，如 "日期"、"定制"、"范围"
     *                    - 配置：定制类型传入选项如 "男,女"，范围类型传入范围表达式如 "(1,100)"
     *                    - 后缀：要拼接到结果后面的字符串，如 "B"
     * @return 拼接后的字符串，如 A2025-09-23B、A男B、A42.00B；
     *         如果输入配置格式错误、类型不支持、配置内容缺失、或递归调用拼接类型，则返回 null 并打印错误日志
     */
    private static Object generateConcatenatedFieldValue(Random random, int rowIdx, String randomVal) {
        if (randomVal == null || randomVal.trim().isEmpty()) {
            log.error("当类型为拼接时，配置(randomVal)不能为空");
            return null;
        }

        // --- 1. 分隔 前缀 和 剩余部分（类型+配置|后缀）---
        String[] prefixAndRest = randomVal.split("@", 2);
        if (prefixAndRest.length < 2) {
            log.error("当类型为拼接时，配置格式错误，应为：前缀@类型[@配置]|后缀，但得到：{}",randomVal);
            return null;
        }

        String prefix = prefixAndRest[0].trim(); // 如 A
        String typeAndRest = prefixAndRest[1].trim(); // 如 定制@男,女|B 或 范围@(1,100)|B 或 日期|B

        // --- 2. 尝试提取 类型 和 配置部分（看是否有第二个 @）---
        String type = null;
        String config = null;
        String suffix = "";

        String[] typeAndConfigAndSuffix = typeAndRest.split("\\|", 2);
        String typeAndConfigPart = typeAndConfigAndSuffix[0].trim(); // 如 定制@男,女 或 范围@(1,100) 或 日期
        suffix = typeAndConfigAndSuffix.length > 1 ? typeAndConfigAndSuffix[1].trim() : ""; // 如 B

        String[] typeAndConfig = typeAndConfigPart.split("@", 2);
        type = typeAndConfig[0].trim(); // 如 定制 或 范围 或 日期

        if (typeAndConfig.length > 1) {
            config = typeAndConfig[1].trim(); // 如 男,女 或 (1,100)
        }

        // --- 3. 处理不同类型 ---
        Object generatedValue;

        if ("拼接".equals(type)) {
            log.error("当类型为拼接时，配置(A@拼接|B 或 A@拼接@xxx|B) 不允许嵌套，避免递归调用栈溢出：{}",randomVal);
            return null;
        } else if ("定制".equals(type)) {
            if (config == null || config.trim().isEmpty()) {
                log.error("当类型为拼接时，配置中定制类型格式错误，应为：A@定制@选项1,选项2|B，但配置为空：{}",randomVal);
                return null;
            }
            generatedValue = generateCustom(random, config); // 调用定制方法，传入 "男,女"
        } else if ("范围".equals(type)) {
            if (config == null || config.trim().isEmpty()) {
                log.error("当类型为拼接时，配置中范围类型格式错误，应为：A@范围@(x,y)|B，但配置为空：{}",randomVal);
                return null;
            }
            if (!config.startsWith("(") || !config.endsWith(")")) {
                log.error("当类型为拼接时，配置中范围类型格式错误，必须用括号括起来，如：(1,100)：{}",randomVal);
                return null;
            }
            generatedValue = generateRangeFieldValue(config); // 调用范围方法，传入 "(1,100)"
        } else {
            // 普通类型：日期、整数、小数、字符串等
            // 构造 FieldConfig，typeDesc = type，其它字段传默认值
            FieldConfig fc = new FieldConfig(
                    "concat_field",
                    type,
                    20,
                    2,
                    null,     // 不使用 randomValues
                    false     // 不是词库字段
            );

            try {
                generatedValue = generateFieldValue(fc, random, rowIdx);
            } catch (Exception e) {
                generatedValue = "[生成[" + type + "]时出错]";
            }
        }

        // --- 4. 拼接 前缀 + 生成的值 + 后缀 ---
        return prefix + (generatedValue != null ? generatedValue.toString() : "") + suffix;
    }

    /**
     * 根据范围表达式生成随机值（支持数字范围、日期范围、日期时间范围）
     * 要求传入的范围表达式必须为："(x,y)"，其中 x 和 y 可以是：
     * - 数字，如 1.5 或 100
     * - 日期，如 2000-01-01
     * - 日期时间，如 2000-01-01 14:30:00
     *
     * @param rangeExpression 范围表达式，如 "(1.5,10.5)"、"(2000-01-01,2050-12-31)"、"(2000-01-01 14:30:00,2050-12-31 23:59:59)"
     * @return 随机值，可能是：
     *         - Double（小数，保留2位）
     *         - String（日期，格式 yyyy-MM-dd）
     *         - String（日期时间，格式 yyyy-MM-dd HH:mm:ss）
     *         - 错误提示字符串（格式错误、解析失败等）
     */
    private static Object generateRangeFieldValue(String rangeExpression) {
        // ✅ 必须用括号括起来
        if (rangeExpression == null || !rangeExpression.startsWith("(") || !rangeExpression.endsWith(")")) {
            log.error("当类型为范围时，必须用括号括起来({})",rangeExpression);
            return null;
        }

        // 去掉括号
        String rangeContent = rangeExpression.substring(1, rangeExpression.length() - 1).trim();
        String[] rangeParts = rangeContent.split("[,，]", 2);

        // 必须是两个值，用逗号分隔
        if (rangeParts.length != 2) {
            log.error("当类型为范围时，必须是两个值，用逗号分隔:{}",rangeExpression);
            return null;
        }

        String lowerStr = rangeParts[0].trim();
        String upperStr = rangeParts[1].trim();

        try {
            // ========== 【1. 数字范围】 ==========
            if (isNumeric(lowerStr) && isNumeric(upperStr)) {
                double lower = Double.parseDouble(lowerStr);
                double upper = Double.parseDouble(upperStr);
                double randomValue = lower + (upper - lower) * Math.random();
                // 保留2位小数
                return BigDecimal.valueOf(randomValue).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
            }
            // ========== 【2. 日期 或 日期时间 范围】 ==========
            else {
                boolean isDateTimeLower = isDateTimeFormat(lowerStr);
                boolean isDateTimeUpper = isDateTimeFormat(upperStr);

                boolean isDateTimeRange = isDateTimeLower && isDateTimeUpper;
                boolean isDateRange = !isDateTimeLower && !isDateTimeUpper && isValidDate(lowerStr) && isValidDate(upperStr);

                if (isDateTimeRange) {
                    // 日期时间范围：如 (2020-01-01 14:30:00, 2030-12-31 23:59:59)
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    Date start = sdf.parse(lowerStr);
                    Date end = sdf.parse(upperStr);
                    Date randomDate = generateRandomDateBetween(start, end);
                    return sdf.format(randomDate);
                } else if (isDateRange) {
                    // 纯日期范围：如 (2020-01-01, 2030-12-31)
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                    Date start = sdf.parse(lowerStr);
                    Date end = sdf.parse(upperStr);
                    Date randomDate = generateRandomDateBetween(start, end);
                    return sdf.format(randomDate);
                } else {
                    // 格式不匹配：比如一个是日期，一个是日期时间，或者完全无法识别
                    log.error("当类型为范围时，格式不匹配:{}",rangeExpression);
                    return null;
                }
            }
        } catch (Exception e) {
            log.error("当类型为范围时，未知错误");
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 判断字符串是否为合法的 yyyy-MM-dd HH:mm:ss 格式（简单正则校验）
     */
    private static boolean isDateTimeFormat(String datetimeStr) {
        if (datetimeStr == null || datetimeStr.length() != 19) return false;
        return datetimeStr.matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}");
    }

    /**
     * 判断字符串是否为数字（支持整数或小数）
     */
    private static boolean isNumeric(String str) {
        if (str == null || str.isEmpty()) return false;
        try {
            Double.parseDouble(str);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    /**
     * 判断字符串是否为合法的 yyyy-MM-dd 格式日期（简单校验）
     */
    private static boolean isValidDate(String dateStr) {
        if (dateStr == null || dateStr.length() != 10) return false;
        return dateStr.matches("\\d{4}-\\d{2}-\\d{2}");
    }

    /**
     * 生成两个日期之间的随机日期
     */
    private static Date generateRandomDateBetween(Date start, Date end) {
        long startMillis = start.getTime();
        long endMillis = end.getTime();
        long randomMillis = startMillis + (long) (Math.random() * (endMillis - startMillis));
        return new Date(randomMillis);
    }

    /**
     * 生成随机的 IPv4 地址，格式如：192.168.1.1
     */
    private static String generateIPv4(Random random) {
        int[] octets = new int[4];
        for (int i = 0; i < 4; i++) {
            octets[i] = 1 + random.nextInt(254); // 避免 0 和 255 开头的一些特殊情况，可根据需求调整
        }
        return octets[0] + "." + octets[1] + "." + octets[2] + "." + octets[3];
    }

    /**
     * 生成随机的简化版 IPv6 地址，格式如：2001:0db8:85a3:0000:0000:8a2e:0370:7334
     * 为了简化，这里生成的是标准的 8 组 4 位 16 进制数，每组用冒号分隔
     */
    private static String generateIPv6(Random random) {
        StringBuilder ipv6 = new StringBuilder();
        for (int i = 0; i < 8; i++) {
            if (i > 0) ipv6.append(":");
            // 每组是 16 进制的 4 位，不足补零
            String group = String.format("%04x", random.nextInt(0x10000));
            ipv6.append(group);
        }
        return ipv6.toString();
    }

    private static Object generateCustom(Random random,String str){
        String[] split = str.split("[,，]");
        return split[random.nextInt(split.length)];
    }

    private static Object generateUrl(Random random, int rowIdx) {
        List<String> domains = LEXICON_MAP.getOrDefault("url", Arrays.asList(
                "https://www.example.com", "http://test.com/home", "https://www.baidu.com",
                "http://www.qq.com", "https://github.com"
        ));
        String domain = domains.get(random.nextInt(domains.size()));
        String path = random.nextBoolean() ? "" : "/page_" + (rowIdx % 100);
        return domain + path;
    }

    private static Object generateAddress(Random random, int rowIdx) {
        List<String> keywords = LEXICON_MAP.getOrDefault("address_keyword", Arrays.asList(
                "北京市海淀区中关村大街", "上海市浦东新区陆家嘴", "广州市天河区珠江新城",
                "深圳市南山区科技园", "成都市武侯区天府软件园"
        ));
        List<String> streets = Arrays.asList("科技园路", "金融城街", "创新大道", "创业路", "软件大道", "商业街", "中心路");
        List<String> buildings = Arrays.asList("1号楼", "2号楼", "A座", "B座", "101室", "202室", "303室", "505室");

        String base = keywords.get(random.nextInt(keywords.size()));
        String street = streets.get(random.nextInt(streets.size()));
        String building = buildings.get(random.nextInt(buildings.size()));

        return base + street + building;
    }

    private static Object generateEnglishContent(Random random, int rowIdx) {
        List<String> subjects = Arrays.asList("The", "This", "That", "Our", "A", "An", "Sample");
        List<String> adjectives = Arrays.asList("test", "random", "new", "sample", "fake", "mock", "valid", "simple");
        List<String> nouns = Arrays.asList("data", "record", "user", "value", "entry", "field", "text", "content", "info", "row");
        List<String> verbs = Arrays.asList("generated", "inserted", "updated", "tested", "processed", "created", "checked", "validated", "saved", "fetched");

        String subj = subjects.get(random.nextInt(subjects.size()));
        String adj = adjectives.get(random.nextInt(adjectives.size()));
        String noun = nouns.get(random.nextInt(nouns.size()));
        String verb = verbs.get(random.nextInt(verbs.size()));

        return String.format("%s %s %s is being %s for test row %d.", subj, adj, noun, verb, rowIdx);
    }


    private static Object generateChineseName(Random random, int rowIdx) {
        // 常见单姓
        List<String> surnames = Arrays.asList(
                "王", "李", "张", "刘", "陈", "杨", "赵", "黄", "周", "吴",
                "徐", "孙", "胡", "朱", "高", "林", "何", "郭", "马", "罗"
        );
        // 复姓（双字姓）
        List<String> doubleSurnames = Arrays.asList("欧阳", "司马", "上官", "诸葛", "东方", "独孤");
        // 常见单字名
        List<String> singleNames = Arrays.asList("伟", "芳", "娜", "秀英", "敏", "静", "丽", "强", "磊", "军", "勇", "洋", "艳", "杰", "娟", "涛", "明", "超", "秀兰", "霞");
        // 常见双字名
        List<String> doubleNames = Arrays.asList(
                "志强", "建华", "淑芬", "国华", "桂英", "秀珍", "建国", "秀兰", "志华", "文革",
                "雨婷", "宇轩", "梓涵", "浩然", "子涵", "欣怡", "梦琪", "紫涵", "天佑", "思涵"
        );

        String surname;
        if (random.nextDouble() < 0.05) { // 5% 概率生成复姓
            surname = doubleSurnames.get(random.nextInt(doubleSurnames.size()));
        } else {
            surname = surnames.get(random.nextInt(surnames.size()));
        }

        String name;
        if (random.nextDouble() < 0.4) { // 40% 概率生成双字名
            name = doubleNames.get(random.nextInt(doubleNames.size()));
        } else { // 60% 概率生成单字名
            name = singleNames.get(random.nextInt(singleNames.size()));
        }

        // 可选：结合行号微调（例如名字后加行号尾字符，仅作示例，不推荐实际业务用）
        // name = name + (rowIdx % 10); // 可选拼接行号后缀增加差异

        return surname + name;
    }

    private static Object generateChineseText(Random random, int rowIdx) {
        List<String> subjects = Arrays.asList("这个", "那个", "今天的", "他的", "她的", "我们的", "测试");
        List<String> actions = Arrays.asList("测试", "运行", "生成", "创建", "插入", "更新", "查询", "展示", "处理", "分析");
        List<String> objects = Arrays.asList("数据", "记录", "表", "字段", "内容", "信息", "结果", "用户", "订单", "配置");
        List<String> adjectives = Arrays.asList("随机", "测试", "模拟", "标准", "有效", "完整", "真实", "新", "临时", "重要");

        String subject = subjects.get(random.nextInt(subjects.size()));
        String adj = adjectives.get(random.nextInt(adjectives.size()));
        String action = actions.get(random.nextInt(actions.size()));
        String obj = objects.get(random.nextInt(objects.size()));

        return String.format("%s%s%s%s正在%s%s", subject, adj, obj, rowIdx % 10, action, obj);
    }


    private static Object generateMobile(Random random, int rowIdx) {
        List<String> prefixes = LEXICON_MAP.getOrDefault("mobile_prefix", Arrays.asList("130", "131", "132", "133", "134", "135", "136", "137", "138", "139"));
        String prefix = prefixes.get(random.nextInt(prefixes.size()));
        // 后8位：行号相关偏移 + 随机，确保不同行不一样
        int suffix = 10000000 + ((rowIdx * 12345) % 90000000) + random.nextInt(10000);
        return prefix + suffix;
    }

    private static Object generateEmail(Random random, int rowIdx) {
        List<String> prefixes = Arrays.asList("user", "test", "demo", "data", "gen", "account", "admin", "info", "contact", "random");
        List<String> domains = LEXICON_MAP.getOrDefault("email_domain", Arrays.asList("gmail.com", "yahoo.com", "hotmail.com", "qq.com", "163.com"));

        String prefix = prefixes.get(random.nextInt(prefixes.size())) + "_" + (rowIdx % 1000);
        String domain = domains.get(random.nextInt(domains.size()));

        return prefix + "@" + domain;
    }

    private static Object generateDate(Random random, int rowIdx) {
        int year = 1970 + random.nextInt(60);
        int month = 1 + random.nextInt(12);
        int day = 1 + random.nextInt(28);
        return String.format("%04d-%02d-%02d", year, month, day);
    }

    // 新增的生成日期时间的方法
    private static Object generateDateTime(Random random, int rowIdx) {
        int year = 1970 + random.nextInt(60);
        int month = 1 + random.nextInt(12);
        int day = 1 + random.nextInt(28);
        int hour = 0 + random.nextInt(24);
        int minute = 0 + random.nextInt(60);
        int second = 0 + random.nextInt(60);

        return String.format("%04d-%02d-%02d %02d:%02d:%02d", year, month, day, hour, minute, second);
    }

    private static Object generateGender(Random random) {
        return LEXICON_MAP.get("gender").get(random.nextInt(LEXICON_MAP.get("gender").size()));
    }

    private static Object generateCountry(Random random) {
        return LEXICON_MAP.get("country").get(random.nextInt(LEXICON_MAP.get("country").size()));
    }

    private static Object generateCurrency(Random random) {
        return LEXICON_MAP.get("currency").get(random.nextInt(LEXICON_MAP.get("currency").size()));
    }

    private static Object generateProductType(Random random) {
        return LEXICON_MAP.get("product_type").get(random.nextInt(LEXICON_MAP.get("product_type").size()));
    }

    // 生成随机小数
    private static Object generateDecimal(Random random, Integer length, Integer scale) {
        if (length == null) length = 10;
        if (scale == null) scale = 2;

        if (length > 0 && scale >= 0 && scale < length) {
            double max = Math.pow(10, length - scale) - 1;
            double value = random.nextDouble() * max;
            BigDecimal bd = new BigDecimal(value).setScale(scale, BigDecimal.ROUND_HALF_UP);
            return bd.doubleValue();
        } else {
            return new BigDecimal(random.nextDouble() * 1000).setScale(2, BigDecimal.ROUND_HALF_UP);
        }
    }

    // 生成随机整数
    private static Object generateInteger(Random random, Integer length, int rowIdx) {
        if (length == null) length = 6;
        int max = (int) Math.min(Math.pow(10, Math.min(length, 10)) - 1, 999999999);
        return random.nextInt(max + 1);
    }

    // 生成随机字符串
    private static Object generateString(Random random, Integer length, int rowIdx) {
        if (length == null) {
            length = 20;
        }

        int minLen = 1;
        int maxLen = Math.min(length, 50);

        // 保证最大长度不小于最小长度
        if (maxLen < minLen) {
            maxLen = minLen;
        }

        int range = maxLen - minLen + 1;
        if (range <= 0) {
            // 极端情况，直接使用 minLen
            range = 1;
        }

        int actualLen = minLen + random.nextInt(range);
        String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < actualLen; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        return sb.toString();
    }

    /**
     * 构建支持多数据库和多字段冲突检测的 INSERT SQL 语句：
     *
     * - MySQL:     使用 INSERT IGNORE INTO ...，基于主键/唯一键自动跳过冲突（不指定字段，但需数据库有唯一约束）
     * - PostgreSQL: 使用 INSERT INTO ... ON CONFLICT (字段1, 字段2, ...) DO NOTHING，支持多字段组合冲突检测
     * - Oracle:    使用 MERGE INTO ... ON (t.字段1 = s.字段1 AND t.字段2 = s.字段2 ...) WHEN NOT MATCHED THEN INSERT ...，支持多字段组合
     * - 其它数据库：使用标准 INSERT INTO ...，无特殊冲突处理
     *
     * @param tableName      表名
     * @param columns        要插入的字段列表，如 ["ip", "user_agent", "create_time"]
     * @param conflictFields 用于冲突检测的字段列表，如 ["ip", "user_agent"]，必须是表上的唯一键或联合唯一索引字段
     * @return 拼接好的、适配当前数据库的 INSERT SQL 语句
     */
    private static String buildInsertSql(String tableName, List<String> columns, List<String> conflictFields) {
        StringBuilder cols = new StringBuilder(), placeholders = new StringBuilder();

        for (int i = 0; i < columns.size(); i++) {
            if (i > 0) {
                cols.append(", ");
                placeholders.append(", ");
            }
            cols.append(columns.get(i));
            placeholders.append("?");
        }

        String baseSql = String.format("INSERT INTO %s (%s) VALUES (%s)", tableName, cols, placeholders);

        if (DATABASE_TYPE == DatabaseType.MYSQL) {
            // MySQL: 使用 INSERT IGNORE，冲突时跳过（基于主键/唯一键，可以是多字段）
            return "INSERT IGNORE " + baseSql.substring("INSERT ".length());

        } else if (DATABASE_TYPE == DatabaseType.POSTGRESQL) {
            // PostgreSQL: 使用 ON CONFLICT (字段1, 字段2, ...) DO NOTHING
            if (conflictFields != null && !conflictFields.isEmpty()) {
                String conflictClause = String.join(", ", conflictFields);
                return String.format(
                        "INSERT INTO %s (%s) VALUES (%s) ON CONFLICT (%s) DO NOTHING",
                        tableName, cols, placeholders, conflictClause
                );
            } else {
                // 未指定冲突字段，使用普通 INSERT（可能因唯一约束报错）
                return baseSql;
            }

        } else if (DATABASE_TYPE == DatabaseType.ORACLE) {
            // Oracle: 使用 MERGE INTO ... ON (t.字段1 = s.字段1 AND t.字段2 = s.字段2 ...) WHEN NOT MATCHED THEN INSERT ...
            if (conflictFields != null && !conflictFields.isEmpty()) {
                // 构建 ON 条件：t.ip = s.ip AND t.user_agent = s.user_agent
                StringBuilder onConditions = new StringBuilder();
                // 构建 USING 子句中的字段：SELECT ? AS ip, ? AS user_agent FROM dual
                StringBuilder usingSelectFields = new StringBuilder();
                List<String> conflictFieldList = conflictFields; // 如 ["ip", "user_agent"]

                for (int i = 0; i < conflictFieldList.size(); i++) {
                    String field = conflictFieldList.get(i);
                    if (i > 0) {
                        onConditions.append(" AND ");
                        usingSelectFields.append(", ");
                    }
                    onConditions.append("t.").append(field).append(" = s.").append(field);
                    usingSelectFields.append("? AS ").append(field);
                }

                // 构建 USING 子句：(SELECT ? AS ip, ? AS user_agent FROM dual)
                String usingClause = String.format("SELECT %s FROM dual", usingSelectFields);

                return String.format(
                        "MERGE INTO %s t " +
                                "USING (%s) s " +
                                "ON (%s) " +
                                "WHEN NOT MATCHED THEN " +
                                "  INSERT (%s) VALUES (%s)",
                        tableName,
                        usingClause,
                        onConditions.toString(),
                        String.join(", ", columns),
                        buildMergeValuesPlaceholders(columns.size())
                );
            } else {
                // 未指定冲突字段，使用普通 INSERT
                return baseSql;
            }

        } else {
            // 其它数据库：标准 INSERT
            return baseSql;
        }
    }

    /**
     * 构建 Oracle MERGE 语句中 VALUES (...) 的占位符，如：?, ?, ?
     * @param count 字段个数
     * @return 占位符字符串，如 "?, ?, ?"
     */
    private static String buildMergeValuesPlaceholders(int count) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < count; i++) {
            if (i > 0) sb.append(", ");
            sb.append("?");
        }
        return sb.toString();
    }

    /**
     * 根据字段数量动态计算合理的批量插入大小
     * @param fieldCount 表的字段数量（即每行数据对应的 ? 占位符数量）
     * @return 批次大小，最小为1
     */
    public static int getDynamicBatchSize(int fieldCount) {
        if (fieldCount <= 0) {
            return 100; // 默认安全值
        }
        int calculated = MAX_JDBC_BATCH_PARAMS / fieldCount;
        return Math.max(1, calculated); // 至少为1
    }
}